﻿// Copyright (c) Inossem. All rights reserved.
// Licensed under the LGPL-3.0 license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DWF.Studio.ScriptEngine
{
    /// <summary>
    /// 脚本引擎加载器基类
    /// </summary>
    [Serializable]
    public abstract class ScriptEngineLoaderBase : IScriptEngineLoader
    {
        protected IScriptEngineIPCService IPCService
        {
            get;
            private set;
        }

        public ScriptEngineLoaderBase(IScriptEngineIPCService ipcService)
        {
            IPCService = ipcService;
        }

        /// <summary>
        /// 执行程序域
        /// </summary>
        protected AppDomain ExcuteAppDomain
        {
            set;
            get;
        }

        /// <summary>
        /// 程序集解析路径
        /// </summary>
        protected string[] AssemblyResolutionPaths
        {
            set;
            get;
        }

        /// <summary>
        /// 获取程序集设置
        /// </summary>
        /// <returns></returns>
        protected abstract AppDomainSetup GetAppDomainSetup();

        /// <summary>
        /// 获取封送的脚本引擎
        /// </summary>
        /// <returns></returns>
        protected abstract IScriptEngine GetUnwrapScriptEngine(IScriptEngineIPCService engineIPCService);

        /// <summary>
        /// 创建程序域
        /// </summary>
        protected void CreateAppDomain()
        {
            AppDomainSetup setup = this.GetAppDomainSetup();

            ExcuteAppDomain = AppDomain.CreateDomain("DWF.Studio.RemoteExcutor", null, setup);
            ExcuteAppDomain.AssemblyResolve += AppDomain_AssemblyResolve;
        }

        /// <summary>
        /// 加载引擎
        /// </summary>
        /// <param name="resolutionPaths"></param>
        /// <returns></returns>
        public virtual IScriptEngine Load(string[] resolutionPaths, ETargetPlatform platform)
        {
            var scriptEngine = Load(resolutionPaths, null, platform);

            return scriptEngine;
        }

        /// <summary>
        /// 加载引擎
        /// </summary>
        /// <param name="resolutionPaths"></param>
        /// <param name="assemblyNames"></param>
        /// <returns></returns>
        public virtual IScriptEngine Load(string[] resolutionPaths, string[] assemblyNames, ETargetPlatform platform)
        {
            AssemblyResolutionPaths = resolutionPaths;
            CreateAppDomain();

            var scriptEngine = GetUnwrapScriptEngine(IPCService) as ScriptEngineBase;

            if (scriptEngine != null)
            {
                scriptEngine.LoadDepencyAssemblies(assemblyNames);
            }

            return scriptEngine;
        }

        /// <summary>
        /// 卸载
        /// </summary>
        public virtual void Unload()
        {
            if (ExcuteAppDomain != null)
            {
                AppDomain.Unload(ExcuteAppDomain);
                ExcuteAppDomain = null;
            }
        }

        protected Assembly AppDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly asm in loadedAssemblies)
            {
                if (asm.FullName.Equals(args.Name))
                    return asm;
            }

            AssemblyName asmInfo = new AssemblyName(args.Name);
            if (AssemblyResolutionPaths != null)
            {
                for (int i = 0; i < this.AssemblyResolutionPaths.Length; i++)
                {
                    string asmFilePath = Path.Combine(this.AssemblyResolutionPaths[i], asmInfo.Name + ".dll");
                    if (File.Exists(asmFilePath))
                    {
                        return Assembly.LoadFrom(asmFilePath);
                    }
                    else
                    {
                        asmFilePath = Path.Combine(this.AssemblyResolutionPaths[i], asmInfo.Name + ".exe");
                        if (File.Exists(asmFilePath))
                            return Assembly.LoadFrom(asmFilePath);
                    }
                }
            }

            return null;
        }

        public IScriptEngine Load(string[] resolutionPaths, string[] assemblyNames, string pipeName, ETargetPlatform platform)
        {
            throw new NotImplementedException();
        }

        public IScriptEngine Load(string engineId)
        {
            throw new NotImplementedException();
        }

        public IScriptEngine Load(string[] resolutionPaths, string[] assemblyNames, Dictionary<string, string> projectProperties, string param, ETargetPlatform platform)
        {
            throw new NotImplementedException();
        }

        public IScriptEngine Load(string[] resolutionPaths, string[] assemblyNames, Dictionary<string, string> projectProperties, Dictionary<string, Dictionary<string, string>> startupProperties, string param, ETargetPlatform platform)
        {
            throw new NotImplementedException();
        }
    }
}
